/*-------------------------------------------------------------------------
 *
 * orderedsetaggs.c
 *		有序集合聚合函数。
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/orderedsetaggs.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include <math.h>

#include "catalog/pg_aggregate.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_type.h"
#include "executor/executor.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "optimizer/optimizer.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "utils/tuplesort.h"


/*
 * 有序集合聚合的通用支持
 *
 * 有序集合聚合的状态分为每组结构（这是返回给 nodeAgg.c 的内部类型转换状态数据）
 * 和每查询结构，后者包含的数据和子对象我们可以在每个查询中只创建一次，因为它们在组之间不会改变。
 * 每查询结构和附属数据生活在执行器的每查询内存上下文中，并在 ExecutorEnd() 时隐式消失。
 *
 * 这些结构在转换函数的第一次调用期间设置。
 * 由于我们允许 nodeAgg.c 合并输入和转换函数相同的有序集合聚合（但不是假设聚合），
 * 因此这些信息不能依赖于特定的聚合（即特定的最终函数），也不能依赖于聚合的直接参数。
 */

typedef struct OSAPerQueryState
{
	/* 该聚合的代表 Aggref： */
	Aggref	   *aggref;
	/* 包含此结构和其他每查询数据的内存上下文： */
	MemoryContext qcontext;
	/* 表达式评估的上下文 */
	ExprContext *econtext;
	/* 我们是否期望在一个组内进行多个最终函数调用？ */
	bool		rescan_needed;

	/* 这些字段仅在累积元组时使用： */

	/* 插入到 sortstate 的元组描述符： */
	TupleDesc	tupdesc;
	/* 我们可以用于插入/提取元组的元组槽： */
	TupleTableSlot *tupslot;
	/* 每排序列的排序信息 */
	int			numSortCols;
	AttrNumber *sortColIdx;
	Oid		   *sortOperators;
	Oid		   *eqOperators;
	Oid		   *sortCollations;
	bool	   *sortNullsFirsts;
	/* 相等运算符调用信息，仅在需要时创建： */
	ExprState  *compareTuple;

	/* 这些字段仅在累积数据项时使用： */

	/* 被排序的数据类型的信息： */
	Oid			sortColType;
	int16		typLen;
	bool		typByVal;
	char		typAlign;
	/* 关于排序顺序的信息： */
	Oid			sortOperator;
	Oid			eqOperator;
	Oid			sortCollation;
	bool		sortNullsFirst;
	/* 相等运算符调用信息，仅在需要时创建： */
	FmgrInfo	equalfn;
} OSAPerQueryState;

typedef struct OSAPerGroupState
{
	/* 指向此聚合的每查询状态的链接： */
	OSAPerQueryState *qstate;
	/* 包含每组数据的内存上下文： */
	MemoryContext gcontext;
	/* 我们正在累积数据的排序对象： */
	Tuplesortstate *sortstate;
	/* 插入到 sortstate 的正常行数： */
	int64		number_of_rows;
	/* 我们是否已经完成了 tuplesort_performsort？ */
	bool		sort_done;
} OSAPerGroupState;

static void fc_ordered_set_shutdown(Datum fc_arg);


/*
 * 为有序集合聚合设置工作状态
 */
static OSAPerGroupState *
fc_ordered_set_startup(FunctionCallInfo fcinfo, bool fc_use_tuples)
{
	OSAPerGroupState *fc_osastate;
	OSAPerQueryState *fc_qstate;
	MemoryContext fc_gcontext;
	MemoryContext fc_oldcontext;
	int			fc_tuplesortopt;

	/*
	 * 检查我们作为聚合被调用（而不是窗口函数），并获取
	 * Agg 节点的组生存期上下文（可能会从组到组改变，因此我们不应将其缓存到每查询状态中）。
	 */
	if (AggCheckCallContext(fcinfo, &fc_gcontext) != AGG_CONTEXT_AGGREGATE)
		elog(ERROR, "ordered-set aggregate called in non-aggregate context");

	/*
	 * 我们在 fn_extra 中保留对每查询状态的链接；如果不存在，
	 * 创建它，并进行我们需要的每查询设置。
	 */
	fc_qstate = (OSAPerQueryState *) fcinfo->flinfo->fn_extra;
	if (fc_qstate == NULL)
	{
		Aggref	   *fc_aggref;
		MemoryContext fc_qcontext;
		List	   *fc_sortlist;
		int			fc_numSortCols;

		/* 获取 Aggref，以便我们可以检查聚合的参数 */
		fc_aggref = AggGetAggref(fcinfo);
		if (!fc_aggref)
			elog(ERROR, "ordered-set aggregate called in non-aggregate context");
		if (!AGGKIND_IS_ORDERED_SET(fc_aggref->aggkind))
			elog(ERROR, "ordered-set aggregate support function called for non-ordered-set aggregate");

		/*
		 * 在 fn_mcxt 中准备每查询结构，我们假设这是
		 * 执行器的每查询上下文；无论如何，它都是存放通过 fn_extra 找到的任何内容的正确地方。
		 */
		fc_qcontext = fcinfo->flinfo->fn_mcxt;
		fc_oldcontext = MemoryContextSwitchTo(fc_qcontext);

		fc_qstate = (OSAPerQueryState *) palloc0(sizeof(OSAPerQueryState));
		fc_qstate->aggref = fc_aggref;
		fc_qstate->qcontext = fc_qcontext;

		/* 如果转移状态是共享的，我们需要支持重新扫描 */
		fc_qstate->rescan_needed = AggStateIsShared(fcinfo);

		/* 提取排序信息 */
		fc_sortlist = fc_aggref->aggorder;
		fc_numSortCols = list_length(fc_sortlist);

		if (fc_use_tuples)
		{
			bool		fc_ishypothetical = (fc_aggref->aggkind == AGGKIND_HYPOTHETICAL);
			ListCell   *fc_lc;
			int			fc_i;

			if (fc_ishypothetical)
				fc_numSortCols++;	/* 为标志列留出空间 */
			fc_qstate->numSortCols = fc_numSortCols;
			fc_qstate->sortColIdx = (AttrNumber *) palloc(fc_numSortCols * sizeof(AttrNumber));
			fc_qstate->sortOperators = (Oid *) palloc(fc_numSortCols * sizeof(Oid));
			fc_qstate->eqOperators = (Oid *) palloc(fc_numSortCols * sizeof(Oid));
			fc_qstate->sortCollations = (Oid *) palloc(fc_numSortCols * sizeof(Oid));
			fc_qstate->sortNullsFirsts = (bool *) palloc(fc_numSortCols * sizeof(bool));

			fc_i = 0;
			foreach(fc_lc, fc_sortlist)
			{
				SortGroupClause *fc_sortcl = (SortGroupClause *) lfirst(fc_lc);
				TargetEntry *fc_tle = get_sortgroupclause_tle(fc_sortcl,
														   fc_aggref->args);

				/* 解析器应该确保这一点 */
				Assert(OidIsValid(fc_sortcl->sortop));

				fc_qstate->sortColIdx[fc_i] = fc_tle->resno;
				fc_qstate->sortOperators[fc_i] = fc_sortcl->sortop;
				fc_qstate->eqOperators[fc_i] = fc_sortcl->eqop;
				fc_qstate->sortCollations[fc_i] = exprCollation((Node *) fc_tle->expr);
				fc_qstate->sortNullsFirsts[fc_i] = fc_sortcl->nulls_first;
				fc_i++;
			}

			if (fc_ishypothetical)
			{
				/* 将整数标志列作为最后的排序列添加 */
				fc_qstate->sortColIdx[fc_i] = list_length(fc_aggref->args) + 1;
				fc_qstate->sortOperators[fc_i] = Int4LessOperator;
				fc_qstate->eqOperators[fc_i] = Int4EqualOperator;
				fc_qstate->sortCollations[fc_i] = InvalidOid;
				fc_qstate->sortNullsFirsts[fc_i] = false;
				fc_i++;
			}

			Assert(fc_i == fc_numSortCols);

			/*
			 * 获取与聚合输入对应的元组描述
			 * （包括排序表达式）聚合的。
			 */
			fc_qstate->tupdesc = ExecTypeFromTL(fc_aggref->args);

			/* 如果我们需要一个标志列，请修改元组描述以包含它 */
			if (fc_ishypothetical)
			{
				TupleDesc	fc_newdesc;
				int			fc_natts = fc_qstate->tupdesc->natts;

				fc_newdesc = CreateTemplateTupleDesc(fc_natts + 1);
				for (fc_i = 1; fc_i <= fc_natts; fc_i++)
					TupleDescCopyEntry(fc_newdesc, fc_i, fc_qstate->tupdesc, fc_i);

				TupleDescInitEntry(fc_newdesc,
								   (AttrNumber) ++fc_natts,
								   "flag",
								   INT4OID,
								   -1,
								   0);

				FreeTupleDesc(fc_qstate->tupdesc);
				fc_qstate->tupdesc = fc_newdesc;
			}

			/* 创建我们将用于存储/检索行的插槽 */
			fc_qstate->tupslot = MakeSingleTupleTableSlot(fc_qstate->tupdesc,
													   &TTSOpsMinimalTuple);
		}
		else
		{
			/* 对单个数据项进行排序 */
			SortGroupClause *fc_sortcl;
			TargetEntry *fc_tle;

			if (fc_numSortCols != 1 || fc_aggref->aggkind == AGGKIND_HYPOTHETICAL)
				elog(ERROR, "ordered-set aggregate support function does not support multiple aggregated columns");

			fc_sortcl = (SortGroupClause *) linitial(fc_sortlist);
			fc_tle = get_sortgroupclause_tle(fc_sortcl, fc_aggref->args);

			/* 解析器应该确保这一点 */
			Assert(OidIsValid(fc_sortcl->sortop));

			/* 保存排序顺序信息 */
			fc_qstate->sortColType = exprType((Node *) fc_tle->expr);
			fc_qstate->sortOperator = fc_sortcl->sortop;
			fc_qstate->eqOperator = fc_sortcl->eqop;
			fc_qstate->sortCollation = exprCollation((Node *) fc_tle->expr);
			fc_qstate->sortNullsFirst = fc_sortcl->nulls_first;

			/* 保存数据类型信息 */
			get_typlenbyvalalign(fc_qstate->sortColType,
								 &fc_qstate->typLen,
								 &fc_qstate->typByVal,
								 &fc_qstate->typAlign);
		}

		fcinfo->flinfo->fn_extra = (void *) fc_qstate;

		MemoryContextSwitchTo(fc_oldcontext);
	}

	/* 现在在组生命周期上下文中构建我们需要的东西 */
	fc_oldcontext = MemoryContextSwitchTo(fc_gcontext);

	fc_osastate = (OSAPerGroupState *) palloc(sizeof(OSAPerGroupState));
	fc_osastate->qstate = fc_qstate;
	fc_osastate->gcontext = fc_gcontext;

	fc_tuplesortopt = TUPLESORT_NONE;

	if (fc_qstate->rescan_needed)
		fc_tuplesortopt |= TUPLESORT_RANDOMACCESS;

	/*
	 * 初始化元组排序对象。
	 */
	if (fc_use_tuples)
		fc_osastate->sortstate = tuplesort_begin_heap(fc_qstate->tupdesc,
												   fc_qstate->numSortCols,
												   fc_qstate->sortColIdx,
												   fc_qstate->sortOperators,
												   fc_qstate->sortCollations,
												   fc_qstate->sortNullsFirsts,
												   work_mem,
												   NULL,
												   fc_tuplesortopt);
	else
		fc_osastate->sortstate = tuplesort_begin_datum(fc_qstate->sortColType,
													fc_qstate->sortOperator,
													fc_qstate->sortCollation,
													fc_qstate->sortNullsFirst,
													work_mem,
													NULL,
													fc_tuplesortopt);

	fc_osastate->number_of_rows = 0;
	fc_osastate->sort_done = false;

	/* 现在注册一个关闭回调，以在组结束时清理事物 */
	AggRegisterCallback(fcinfo,
						fc_ordered_set_shutdown,
						PointerGetDatum(fc_osastate));

	MemoryContextSwitchTo(fc_oldcontext);

	return fc_osastate;
}

/*
 * 在有序集合聚合的评估完成时进行清理。
 *
 * 我们不需要关心在每个组内存上下文中释放对象，
 * 因为无论如何它将被nodeAgg.c重置；也不应该在每个查询上下文中释放任何东西，
 * 如果这是最后一个组，将被ExecutorEnd清除。 但是我们必须小心释放任何潜在的
 * 非内存资源。
 *
 * 在我们不期望多次调用finalfn的情况下，
 * 我们可以说可以依赖finalfn进行清理；但是，如果我们以任何方式这样做，
 * 它会更简单、更易于测试。
 */
static void fc_ordered_set_shutdown(Datum fc_arg)
{
	OSAPerGroupState *fc_osastate = (OSAPerGroupState *) DatumGetPointer(fc_arg);

	/* 元组排序对象可能有临时文件。 */
	if (fc_osastate->sortstate)
		tuplesort_end(fc_osastate->sortstate);
	fc_osastate->sortstate = NULL;
	/* 元组插槽可能无法保持锁定，但让我们小心点。 */
	if (fc_osastate->qstate->tupslot)
		ExecClearTuple(fc_osastate->qstate->tupslot);
}


/*
 * 有序集合聚合的通用过渡函数
 * 只有一个输入列，我们希望抑制空值
 */
Datum ordered_set_transition(PG_FUNCTION_ARGS)
{
	OSAPerGroupState *fc_osastate;

	/* 如果是第一次调用，请创建过渡状态工作区 */
	if (PG_ARGISNULL(0))
		fc_osastate = fc_ordered_set_startup(fcinfo, false);
	else
		fc_osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);

	/* 将数据项加载到元组排序对象中，但仅当它不为null时 */
	if (!PG_ARGISNULL(1))
	{
		tuplesort_putdatum(fc_osastate->sortstate, PG_GETARG_DATUM(1), false);
		fc_osastate->number_of_rows++;
	}

	PG_RETURN_POINTER(fc_osastate);
}

/*
 * 有序集合聚合的通用过渡函数
 * 具有（可能）多个聚合输入列
 */
Datum ordered_set_transition_multi(PG_FUNCTION_ARGS)
{
	OSAPerGroupState *fc_osastate;
	TupleTableSlot *fc_slot;
	int			fc_nargs;
	int			fc_i;

	/* 如果是第一次调用，请创建过渡状态工作区 */
	if (PG_ARGISNULL(0))
		fc_osastate = fc_ordered_set_startup(fcinfo, true);
	else
		fc_osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);

	/* 从除过渡值外的所有其他输入中形成一个元组 */
	fc_slot = fc_osastate->qstate->tupslot;
	ExecClearTuple(fc_slot);
	fc_nargs = PG_NARGS() - 1;
	for (fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		fc_slot->tts_values[fc_i] = PG_GETARG_DATUM(fc_i + 1);
		fc_slot->tts_isnull[fc_i] = PG_ARGISNULL(fc_i + 1);
	}
	if (fc_osastate->qstate->aggref->aggkind == AGGKIND_HYPOTHETICAL)
	{
		/* 添加零标志值以标记此行为正常输入行 */
		fc_slot->tts_values[fc_i] = Int32GetDatum(0);
		fc_slot->tts_isnull[fc_i] = false;
		fc_i++;
	}
	Assert(fc_i == fc_slot->tts_tupleDescriptor->natts);
	ExecStoreVirtualTuple(fc_slot);

	/* 将行加载到元组排序对象中 */
	tuplesort_puttupleslot(fc_osastate->sortstate, fc_slot);
	fc_osastate->number_of_rows++;

	PG_RETURN_POINTER(fc_osastate);
}


/*
 * percentile_disc(float8) 在组内(anyelement) - 离散百分位
 */
Datum percentile_disc_final(PG_FUNCTION_ARGS)
{
	OSAPerGroupState *fc_osastate;
	double		fc_percentile;
	Datum		fc_val;
	bool		fc_isnull;
	int64		fc_rownum;

	Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);

	/* 获取并检查百分位参数 */
	if (PG_ARGISNULL(1))
		PG_RETURN_NULL();

	fc_percentile = PG_GETARG_FLOAT8(1);

	if (fc_percentile < 0 || fc_percentile > 1 || isnan(fc_percentile))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("percentile value %g is not between 0 and 1",
						fc_percentile)));

	/* 如果没有常规行，则结果为NULL */
	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	fc_osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);

	/* number_of_rows 如果我们只看到NULL输入值，则可能为零 */
	if (fc_osastate->number_of_rows == 0)
		PG_RETURN_NULL();

	/* 完成排序，或如果我们已经完成则重新扫描 */
	if (!fc_osastate->sort_done)
	{
		tuplesort_performsort(fc_osastate->sortstate);
		fc_osastate->sort_done = true;
	}
	else
		tuplesort_rescan(fc_osastate->sortstate);

	/* ----------
	 * 我们需要最小的K，使得 (K/N) >= 百分位。
	 * N>0，因此 K >= N*百分位，因此 K = ceil(N*百分位)。
	 * 所以我们跳过K-1行（如果K>0）并返回下一行。
	 * ----------
	 */
	fc_rownum = (int64) ceil(fc_percentile * fc_osastate->number_of_rows);
	Assert(fc_rownum <= fc_osastate->number_of_rows);

	if (fc_rownum > 1)
	{
		if (!tuplesort_skiptuples(fc_osastate->sortstate, fc_rownum - 1, true))
			elog(ERROR, "missing row in percentile_disc");
	}

	if (!tuplesort_getdatum(fc_osastate->sortstate, true, &fc_val, &fc_isnull, NULL))
		elog(ERROR, "missing row in percentile_disc");

	/* 我们不应该存储任何空值，但无论如何都要做对的事情 */
	if (fc_isnull)
		PG_RETURN_NULL();
	else
		PG_RETURN_DATUM(fc_val);
}


/*
 * 对于percentile_cont，我们需要一种在连续
 * 值之间插值的方法。使用一个帮助函数来实现，
 * 以便在不同类型之间共享其余代码。
 */
typedef Datum (*LerpFunc) (Datum lo, Datum hi, double pct);

static Datum fc_float8_lerp(Datum fc_lo, Datum fc_hi, double fc_pct)
{
	double		fc_loval = DatumGetFloat8(fc_lo);
	double		fc_hival = DatumGetFloat8(fc_hi);

	return Float8GetDatum(fc_loval + (fc_pct * (fc_hival - fc_loval)));
}

static Datum fc_interval_lerp(Datum fc_lo, Datum fc_hi, double fc_pct)
{
	Datum		fc_diff_result = DirectFunctionCall2(interval_mi, fc_hi, fc_lo);
	Datum		fc_mul_result = DirectFunctionCall2(interval_mul,
												 fc_diff_result,
												 Float8GetDatumFast(fc_pct));

	return DirectFunctionCall2(interval_pl, fc_mul_result, fc_lo);
}

/*
 * 连续百分位
 */
static Datum fc_percentile_cont_final_common(FunctionCallInfo fcinfo,
							 Oid fc_expect_type,
							 LerpFunc fc_lerpfunc)
{
	OSAPerGroupState *fc_osastate;
	double		fc_percentile;
	int64		fc_first_row = 0;
	int64		fc_second_row = 0;
	Datum		fc_val;
	Datum		fc_first_val;
	Datum		fc_second_val;
	double		fc_proportion;
	bool		fc_isnull;

	Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);

	/* 获取并检查百分位参数 */
	if (PG_ARGISNULL(1))
		PG_RETURN_NULL();

	fc_percentile = PG_GETARG_FLOAT8(1);

	if (fc_percentile < 0 || fc_percentile > 1 || isnan(fc_percentile))
		ereport(ERROR,
				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
				 errmsg("percentile value %g is not between 0 and 1",
						fc_percentile)));

	/* 如果没有常规行，则结果为NULL */
	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	fc_osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);

	/* number_of_rows 如果我们只看到NULL输入值，则可能为零 */
	if (fc_osastate->number_of_rows == 0)
		PG_RETURN_NULL();

	Assert(fc_expect_type == fc_osastate->qstate->sortColType);

	/* 完成排序，或如果我们已经完成则重新扫描 */
	if (!fc_osastate->sort_done)
	{
		tuplesort_performsort(fc_osastate->sortstate);
		fc_osastate->sort_done = true;
	}
	else
		tuplesort_rescan(fc_osastate->sortstate);

	fc_first_row = floor(fc_percentile * (fc_osastate->number_of_rows - 1));
	fc_second_row = ceil(fc_percentile * (fc_osastate->number_of_rows - 1));

	Assert(fc_first_row < fc_osastate->number_of_rows);

	if (!tuplesort_skiptuples(fc_osastate->sortstate, fc_first_row, true))
		elog(ERROR, "missing row in percentile_cont");

	if (!tuplesort_getdatum(fc_osastate->sortstate, true, &fc_first_val, &fc_isnull, NULL))
		elog(ERROR, "missing row in percentile_cont");
	if (fc_isnull)
		PG_RETURN_NULL();

	if (fc_first_row == fc_second_row)
	{
		fc_val = fc_first_val;
	}
	else
	{
		if (!tuplesort_getdatum(fc_osastate->sortstate, true, &fc_second_val, &fc_isnull, NULL))
			elog(ERROR, "missing row in percentile_cont");

		if (fc_isnull)
			PG_RETURN_NULL();

		fc_proportion = (fc_percentile * (fc_osastate->number_of_rows - 1)) - fc_first_row;
		fc_val = fc_lerpfunc(fc_first_val, fc_second_val, fc_proportion);
	}

	PG_RETURN_DATUM(fc_val);
}

/*
 * percentile_cont(float8) within group (float8)	- 连续百分位
 */
Datum percentile_cont_float8_final(PG_FUNCTION_ARGS)
{
	return fc_percentile_cont_final_common(fcinfo, FLOAT8OID, fc_float8_lerp);
}

/*
 * percentile_cont(float8) within group (interval)	- 连续百分位
 */
Datum percentile_cont_interval_final(PG_FUNCTION_ARGS)
{
	return fc_percentile_cont_final_common(fcinfo, INTERVALOID, fc_interval_lerp);
}


/*
 * 处理百分位数组的支持代码
 *
 * 注意：在每个 pct_info 条目中，second_row 应该等于或
 * 正好比 first_row 大 1。
 */
struct pct_info
{
	int64		first_row;		/* 取样的第一行 */
	int64		second_row;		/* 可能的第二行以供取样 */
	double		proportion;		/* 插值分数 */
	int			idx;			/* 此项目在原始数组中的索引 */
};

/*
 * 排序比较器，以 first_row 然后 second_row 排序 pct_infos
 */
static int fc_pct_info_cmp(const void *fc_pa, const void *fc_pb)
{
	const struct pct_info *fc_a = (const struct pct_info *) fc_pa;
	const struct pct_info *fc_b = (const struct pct_info *) fc_pb;

	if (fc_a->first_row != fc_b->first_row)
		return (fc_a->first_row < fc_b->first_row) ? -1 : 1;
	if (fc_a->second_row != fc_b->second_row)
		return (fc_a->second_row < fc_b->second_row) ? -1 : 1;
	return 0;
}

/*
 * 构建数组，显示要为百分位取样的行。
 */
static struct pct_info *
fc_setup_pct_info(int fc_num_percentiles,
			   Datum *fc_percentiles_datum,
			   bool *fc_percentiles_null,
			   int64 fc_rowcount,
			   bool fc_continuous)
{
	struct pct_info *fc_pct_info;
	int			fc_i;

	fc_pct_info = (struct pct_info *) palloc(fc_num_percentiles * sizeof(struct pct_info));

	for (fc_i = 0; fc_i < fc_num_percentiles; fc_i++)
	{
		fc_pct_info[fc_i].idx = fc_i;

		if (fc_percentiles_null[fc_i])
		{
			/* 数组中任何 NULL 的虚拟条目 */
			fc_pct_info[fc_i].first_row = 0;
			fc_pct_info[fc_i].second_row = 0;
			fc_pct_info[fc_i].proportion = 0;
		}
		else
		{
			double		fc_p = DatumGetFloat8(fc_percentiles_datum[fc_i]);

			if (fc_p < 0 || fc_p > 1 || isnan(fc_p))
				ereport(ERROR,
						(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
						 errmsg("percentile value %g is not between 0 and 1",
								fc_p)));

			if (fc_continuous)
			{
				fc_pct_info[fc_i].first_row = 1 + floor(fc_p * (fc_rowcount - 1));
				fc_pct_info[fc_i].second_row = 1 + ceil(fc_p * (fc_rowcount - 1));
				fc_pct_info[fc_i].proportion = (fc_p * (fc_rowcount - 1)) - floor(fc_p * (fc_rowcount - 1));
			}
			else
			{
				/*----------
				 * 我们需要最小的 K 使得 (K/N) >= 百分位。
				 * N>0，因此 K >= N*百分位，因此
				 * K = ceil(N*百分位)；但不得少于 1。
				 *----------
				 */
				int64		fc_row = (int64) ceil(fc_p * fc_rowcount);

				fc_row = Max(1, fc_row);
				fc_pct_info[fc_i].first_row = fc_row;
				fc_pct_info[fc_i].second_row = fc_row;
				fc_pct_info[fc_i].proportion = 0;
			}
		}
	}

	/*
	 * 参数数组不一定按排序顺序排列，但我们需要
	 * 按顺序访问这些行，因此按 first_row/second_row 排序。
	 */
	qsort(fc_pct_info, fc_num_percentiles, sizeof(struct pct_info), fc_pct_info_cmp);

	return fc_pct_info;
}

/*
 * percentile_disc(float8[]) within group (anyelement)	- 离散百分位
 */
Datum percentile_disc_multi_final(PG_FUNCTION_ARGS)
{
	OSAPerGroupState *fc_osastate;
	ArrayType  *fc_param;
	Datum	   *fc_percentiles_datum;
	bool	   *fc_percentiles_null;
	int			fc_num_percentiles;
	struct pct_info *fc_pct_info;
	Datum	   *fc_result_datum;
	bool	   *fc_result_isnull;
	int64		fc_rownum = 0;
	Datum		fc_val = (Datum) 0;
	bool		fc_isnull = true;
	int			fc_i;

	Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);

	/* 如果没有常规行，则结果为NULL */
	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	fc_osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);

	/* number_of_rows 如果我们只看到NULL输入值，则可能为零 */
	if (fc_osastate->number_of_rows == 0)
		PG_RETURN_NULL();

	/* 解构百分位数组输入 */
	if (PG_ARGISNULL(1))
		PG_RETURN_NULL();
	fc_param = PG_GETARG_ARRAYTYPE_P(1);

	deconstruct_array(fc_param, FLOAT8OID,
	/* 硬编码类型信息 float8 */
					  sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE,
					  &fc_percentiles_datum,
					  &fc_percentiles_null,
					  &fc_num_percentiles);

	if (fc_num_percentiles == 0)
		PG_RETURN_POINTER(construct_empty_array(fc_osastate->qstate->sortColType));

	fc_pct_info = fc_setup_pct_info(fc_num_percentiles,
							  fc_percentiles_datum,
							  fc_percentiles_null,
							  fc_osastate->number_of_rows,
							  false);

	fc_result_datum = (Datum *) palloc(fc_num_percentiles * sizeof(Datum));
	fc_result_isnull = (bool *) palloc(fc_num_percentiles * sizeof(bool));

	/*
	 * 首先处理参数数组中的任何 NULL - 这些被排序到
	 * 前面在行=0，所以将相应的结果索引设置为 NULL
	 */
	for (fc_i = 0; fc_i < fc_num_percentiles; fc_i++)
	{
		int			fc_idx = fc_pct_info[fc_i].idx;

		if (fc_pct_info[fc_i].first_row > 0)
			break;

		fc_result_datum[fc_idx] = (Datum) 0;
		fc_result_isnull[fc_idx] = true;
	}

	/*
	 * 如果处理完 NULL 后还有剩余，则处理输入
	 * 并提取所需值
	 */
	if (fc_i < fc_num_percentiles)
	{
		/* 完成排序，或如果我们已经完成则重新扫描 */
		if (!fc_osastate->sort_done)
		{
			tuplesort_performsort(fc_osastate->sortstate);
			fc_osastate->sort_done = true;
		}
		else
			tuplesort_rescan(fc_osastate->sortstate);

		for (; fc_i < fc_num_percentiles; fc_i++)
		{
			int64		fc_target_row = fc_pct_info[fc_i].first_row;
			int			fc_idx = fc_pct_info[fc_i].idx;

			/* 前进到目标行，如果尚未到达 */
			if (fc_target_row > fc_rownum)
			{
				if (!tuplesort_skiptuples(fc_osastate->sortstate, fc_target_row - fc_rownum - 1, true))
					elog(ERROR, "missing row in percentile_disc");

				if (!tuplesort_getdatum(fc_osastate->sortstate, true, &fc_val, &fc_isnull, NULL))
					elog(ERROR, "missing row in percentile_disc");

				fc_rownum = fc_target_row;
			}

			fc_result_datum[fc_idx] = fc_val;
			fc_result_isnull[fc_idx] = fc_isnull;
		}
	}

	/* 我们令输出数组与输入相同形状 */
	PG_RETURN_POINTER(construct_md_array(fc_result_datum, fc_result_isnull,
										 ARR_NDIM(fc_param),
										 ARR_DIMS(fc_param),
										 ARR_LBOUND(fc_param),
										 fc_osastate->qstate->sortColType,
										 fc_osastate->qstate->typLen,
										 fc_osastate->qstate->typByVal,
										 fc_osastate->qstate->typAlign));
}

/*
 * percentile_cont(float8[]) within group ()	- 连续百分位
 */
static Datum fc_percentile_cont_multi_final_common(FunctionCallInfo fcinfo,
								   Oid fc_expect_type,
								   int16 fc_typLen, bool fc_typByVal, char fc_typAlign,
								   LerpFunc fc_lerpfunc)
{
	OSAPerGroupState *fc_osastate;
	ArrayType  *fc_param;
	Datum	   *fc_percentiles_datum;
	bool	   *fc_percentiles_null;
	int			fc_num_percentiles;
	struct pct_info *fc_pct_info;
	Datum	   *fc_result_datum;
	bool	   *fc_result_isnull;
	int64		fc_rownum = 0;
	Datum		fc_first_val = (Datum) 0;
	Datum		fc_second_val = (Datum) 0;
	bool		fc_isnull;
	int			fc_i;

	Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);

	/* 如果没有常规行，则结果为NULL */
	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	fc_osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);

	/* number_of_rows 如果我们只看到NULL输入值，则可能为零 */
	if (fc_osastate->number_of_rows == 0)
		PG_RETURN_NULL();

	Assert(fc_expect_type == fc_osastate->qstate->sortColType);

	/* 解构百分位数组输入 */
	if (PG_ARGISNULL(1))
		PG_RETURN_NULL();
	fc_param = PG_GETARG_ARRAYTYPE_P(1);

	deconstruct_array(fc_param, FLOAT8OID,
	/* 硬编码类型信息 float8 */
					  sizeof(float8), FLOAT8PASSBYVAL, TYPALIGN_DOUBLE,
					  &fc_percentiles_datum,
					  &fc_percentiles_null,
					  &fc_num_percentiles);

	if (fc_num_percentiles == 0)
		PG_RETURN_POINTER(construct_empty_array(fc_osastate->qstate->sortColType));

	fc_pct_info = fc_setup_pct_info(fc_num_percentiles,
							  fc_percentiles_datum,
							  fc_percentiles_null,
							  fc_osastate->number_of_rows,
							  true);

	fc_result_datum = (Datum *) palloc(fc_num_percentiles * sizeof(Datum));
	fc_result_isnull = (bool *) palloc(fc_num_percentiles * sizeof(bool));

	/*
	 * 首先处理参数数组中的任何 NULL - 这些被排序到
	 * 前面在行=0，所以将相应的结果索引设置为 NULL
	 */
	for (fc_i = 0; fc_i < fc_num_percentiles; fc_i++)
	{
		int			fc_idx = fc_pct_info[fc_i].idx;

		if (fc_pct_info[fc_i].first_row > 0)
			break;

		fc_result_datum[fc_idx] = (Datum) 0;
		fc_result_isnull[fc_idx] = true;
	}

	/*
	 * 如果处理完 NULL 后还有剩余，则处理输入
	 * 并提取所需值
	 */
	if (fc_i < fc_num_percentiles)
	{
		/* 完成排序，或如果我们已经完成则重新扫描 */
		if (!fc_osastate->sort_done)
		{
			tuplesort_performsort(fc_osastate->sortstate);
			fc_osastate->sort_done = true;
		}
		else
			tuplesort_rescan(fc_osastate->sortstate);

		for (; fc_i < fc_num_percentiles; fc_i++)
		{
			int64		fc_first_row = fc_pct_info[fc_i].first_row;
			int64		fc_second_row = fc_pct_info[fc_i].second_row;
			int			fc_idx = fc_pct_info[fc_i].idx;

			/*
			 * 如果尚未到达，则前进到 first_row。请注意，我们可能
			 * 已经有行号超出 first_row，在这种情况下 first_val
			 * 已经正确。（当在相同的两行之间插值时会发生这种情况，
			 * 这与之前的百分位相同。）
			 */
			if (fc_first_row > fc_rownum)
			{
				if (!tuplesort_skiptuples(fc_osastate->sortstate, fc_first_row - fc_rownum - 1, true))
					elog(ERROR, "missing row in percentile_cont");

				if (!tuplesort_getdatum(fc_osastate->sortstate, true, &fc_first_val,
										&fc_isnull, NULL) || fc_isnull)
					elog(ERROR, "missing row in percentile_cont");

				fc_rownum = fc_first_row;
				/* 始终将 second_val 前进到最新输入值 */
				fc_second_val = fc_first_val;
			}
			else if (fc_first_row == fc_rownum)
			{
				/*
				 * 我们已经在所需行上，因此我们必须先前
				 * 已读取其值到 second_val（也可能是 first_val，
				 * 但这个赋值在这种情况下是无害的）。
				 */
				fc_first_val = fc_second_val;
			}

			/* 如果需要，获取 second_row */
			if (fc_second_row > fc_rownum)
			{
				if (!tuplesort_getdatum(fc_osastate->sortstate, true, &fc_second_val,
										&fc_isnull, NULL) || fc_isnull)
					elog(ERROR, "missing row in percentile_cont");
				fc_rownum++;
			}
			/* 我们现在当然应该正好在 second_row 上 */
			Assert(fc_second_row == fc_rownum);

			/* 计算合适的结果 */
			if (fc_second_row > fc_first_row)
				fc_result_datum[fc_idx] = fc_lerpfunc(fc_first_val, fc_second_val,
											 fc_pct_info[fc_i].proportion);
			else
				fc_result_datum[fc_idx] = fc_first_val;

			fc_result_isnull[fc_idx] = false;
		}
	}

	/* 我们令输出数组与输入相同形状 */
	PG_RETURN_POINTER(construct_md_array(fc_result_datum, fc_result_isnull,
										 ARR_NDIM(fc_param),
										 ARR_DIMS(fc_param), ARR_LBOUND(fc_param),
										 fc_expect_type,
										 fc_typLen,
										 fc_typByVal,
										 fc_typAlign));
}

/*
 * percentile_cont(float8[]) within group (float8)	- 连续百分位
 */
Datum percentile_cont_float8_multi_final(PG_FUNCTION_ARGS)
{
	return fc_percentile_cont_multi_final_common(fcinfo,
											  FLOAT8OID,
	/* 硬编码类型信息 float8 */
											  sizeof(float8),
											  FLOAT8PASSBYVAL,
											  TYPALIGN_DOUBLE,
											  fc_float8_lerp);
}

/*
 * percentile_cont(float8[]) within group (interval)  - 连续百分位
 */
Datum percentile_cont_interval_multi_final(PG_FUNCTION_ARGS)
{
	return fc_percentile_cont_multi_final_common(fcinfo,
											  INTERVALOID,
	/* 硬编码类型信息 interval */
											  16, false, TYPALIGN_DOUBLE,
											  fc_interval_lerp);
}


/*
 * mode() within group (anyelement) - 最常见的值
 */
Datum mode_final(PG_FUNCTION_ARGS)
{
	OSAPerGroupState *fc_osastate;
	Datum		fc_val;
	bool		fc_isnull;
	Datum		fc_mode_val = (Datum) 0;
	int64		fc_mode_freq = 0;
	Datum		fc_last_val = (Datum) 0;
	int64		fc_last_val_freq = 0;
	bool		fc_last_val_is_mode = false;
	FmgrInfo   *fc_equalfn;
	Datum		fc_abbrev_val = (Datum) 0;
	Datum		fc_last_abbrev_val = (Datum) 0;
	bool		fc_shouldfree;

	Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);

	/* 如果没有常规行，则结果为NULL */
	if (PG_ARGISNULL(0))
		PG_RETURN_NULL();

	fc_osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);

	/* number_of_rows 如果我们只看到NULL输入值，则可能为零 */
	if (fc_osastate->number_of_rows == 0)
		PG_RETURN_NULL();

	/* 查找数据类型的相等性函数，如果我们还没有查找的话 */
	fc_equalfn = &(fc_osastate->qstate->equalfn);
	if (!OidIsValid(fc_equalfn->fn_oid))
		fmgr_info_cxt(get_opcode(fc_osastate->qstate->eqOperator), fc_equalfn,
					  fc_osastate->qstate->qcontext);

	fc_shouldfree = !(fc_osastate->qstate->typByVal);

	/* 完成排序，或如果我们已经完成则重新扫描 */
	if (!fc_osastate->sort_done)
	{
		tuplesort_performsort(fc_osastate->sortstate);
		fc_osastate->sort_done = true;
	}
	else
		tuplesort_rescan(fc_osastate->sortstate);

	/* 扫描元组并计算频率 */
	while (tuplesort_getdatum(fc_osastate->sortstate, true, &fc_val, &fc_isnull, &fc_abbrev_val))
	{
		/* 我们不期望有任何空值，但如果找到则忽略它们 */
		if (fc_isnull)
			continue;

		if (fc_last_val_freq == 0)
		{
			/* 第一个非空值 - 这是目前的众数 */
			fc_mode_val = fc_last_val = fc_val;
			fc_mode_freq = fc_last_val_freq = 1;
			fc_last_val_is_mode = true;
			fc_last_abbrev_val = fc_abbrev_val;
		}
		else if (fc_abbrev_val == fc_last_abbrev_val &&
				 DatumGetBool(FunctionCall2Coll(fc_equalfn, PG_GET_COLLATION(), fc_val, fc_last_val)))
		{
			/* 值等于前一个值，计数它 */
			if (fc_last_val_is_mode)
				fc_mode_freq++;	/* 不需要维护 last_val_freq */
			else if (++fc_last_val_freq > fc_mode_freq)
			{
				/* last_val 成为新的众数 */
				if (fc_shouldfree)
					pfree(DatumGetPointer(fc_mode_val));
				fc_mode_val = fc_last_val;
				fc_mode_freq = fc_last_val_freq;
				fc_last_val_is_mode = true;
			}
			if (fc_shouldfree)
				pfree(DatumGetPointer(fc_val));
		}
		else
		{
			/* val 应该替换 last_val */
			if (fc_shouldfree && !fc_last_val_is_mode)
				pfree(DatumGetPointer(fc_last_val));
			fc_last_val = fc_val;
			/* 通过重复使用缩写键来避免相等性函数调用 */
			fc_last_abbrev_val = fc_abbrev_val;
			fc_last_val_freq = 1;
			fc_last_val_is_mode = false;
		}

		CHECK_FOR_INTERRUPTS();
	}

	if (fc_shouldfree && !fc_last_val_is_mode)
		pfree(DatumGetPointer(fc_last_val));

	if (fc_mode_freq)
		PG_RETURN_DATUM(fc_mode_val);
	else
		PG_RETURN_NULL();
}


/*
 * 通用代码以检查假设集函数的参数是否合理。没有必要
 * 提供友好的错误信息，这些只能在某人搞错聚合定义的情况下发生。
 * 然而，出于安全原因，需要进行这些检查。
 */
static void fc_hypothetical_check_argtypes(FunctionCallInfo fcinfo, int fc_nargs,
							TupleDesc fc_tupdesc)
{
	int			fc_i;

	/* 检查我们是否有一个 int4 标志列 */
	if (!fc_tupdesc ||
		(fc_nargs + 1) != fc_tupdesc->natts ||
		TupleDescAttr(fc_tupdesc, fc_nargs)->atttypid != INT4OID)
		elog(ERROR, "type mismatch in hypothetical-set function");

	/* 检查直接参数与聚合参数在类型上是否匹配 */
	for (fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_tupdesc, fc_i);

		if (get_fn_expr_argtype(fcinfo->flinfo, fc_i + 1) != fc_attr->atttypid)
			elog(ERROR, "type mismatch in hypothetical-set function");
	}
}

/*
 * 计算假设行的排名
 *
 * 标志应该是 -1 以将假设行排在其同类之前，或 +1
 * 以在其后面排序。
 * 常规行的总数返回到 *number_of_rows 中。
 */
static int64 fc_hypothetical_rank_common(FunctionCallInfo fcinfo, int fc_flag,
						 int64 *fc_number_of_rows)
{
	int			fc_nargs = PG_NARGS() - 1;
	int64		fc_rank = 1;
	OSAPerGroupState *fc_osastate;
	TupleTableSlot *fc_slot;
	int			fc_i;

	Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);

	/* 如果没有常规行，则排名总是 1 */
	if (PG_ARGISNULL(0))
	{
		*fc_number_of_rows = 0;
		return 1;
	}

	fc_osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
	*fc_number_of_rows = fc_osastate->number_of_rows;

	/* 调整 nargs 为直接（或聚合）参数的数量 */
	if (fc_nargs % 2 != 0)
		elog(ERROR, "wrong number of arguments in hypothetical-set function");
	fc_nargs /= 2;

	fc_hypothetical_check_argtypes(fcinfo, fc_nargs, fc_osastate->qstate->tupdesc);

	/* 因为我们需要一个假设行，所以不能共享过渡状态 */
	Assert(!fc_osastate->sort_done);

	/* 将假设行插入排序中 */
	fc_slot = fc_osastate->qstate->tupslot;
	ExecClearTuple(fc_slot);
	for (fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		fc_slot->tts_values[fc_i] = PG_GETARG_DATUM(fc_i + 1);
		fc_slot->tts_isnull[fc_i] = PG_ARGISNULL(fc_i + 1);
	}
	fc_slot->tts_values[fc_i] = Int32GetDatum(fc_flag);
	fc_slot->tts_isnull[fc_i] = false;
	ExecStoreVirtualTuple(fc_slot);

	tuplesort_puttupleslot(fc_osastate->sortstate, fc_slot);

	/* 完成排序 */
	tuplesort_performsort(fc_osastate->sortstate);
	fc_osastate->sort_done = true;

	/* 迭代，直到找到假设行 */
	while (tuplesort_gettupleslot(fc_osastate->sortstate, true, true, fc_slot, NULL))
	{
		bool		fc_isnull;
		Datum		fc_d = slot_getattr(fc_slot, fc_nargs + 1, &fc_isnull);

		if (!fc_isnull && DatumGetInt32(fc_d) != 0)
			break;

		fc_rank++;

		CHECK_FOR_INTERRUPTS();
	}

	ExecClearTuple(fc_slot);

	return fc_rank;
}


/*
 * rank()  - 假设行的排名
 */
Datum hypothetical_rank_final(PG_FUNCTION_ARGS)
{
	int64		fc_rank;
	int64		fc_rowcount;

	fc_rank = fc_hypothetical_rank_common(fcinfo, -1, &fc_rowcount);

	PG_RETURN_INT64(fc_rank);
}

/*
 * percent_rank()	- 假设行的百分位排名
 */
Datum hypothetical_percent_rank_final(PG_FUNCTION_ARGS)
{
	int64		fc_rank;
	int64		fc_rowcount;
	double		fc_result_val;

	fc_rank = fc_hypothetical_rank_common(fcinfo, -1, &fc_rowcount);

	if (fc_rowcount == 0)
		PG_RETURN_FLOAT8(0);

	fc_result_val = (double) (fc_rank - 1) / (double) (fc_rowcount);

	PG_RETURN_FLOAT8(fc_result_val);
}

/*
 * cume_dist()	- 假设行的累积分布
 */
Datum hypothetical_cume_dist_final(PG_FUNCTION_ARGS)
{
	int64		fc_rank;
	int64		fc_rowcount;
	double		fc_result_val;

	fc_rank = fc_hypothetical_rank_common(fcinfo, 1, &fc_rowcount);

	fc_result_val = (double) (fc_rank) / (double) (fc_rowcount + 1);

	PG_RETURN_FLOAT8(fc_result_val);
}

/*
 * dense_rank() - 没有排名间隙的假设行排名
 */
Datum hypothetical_dense_rank_final(PG_FUNCTION_ARGS)
{
	ExprContext *fc_econtext;
	ExprState  *fc_compareTuple;
	int			fc_nargs = PG_NARGS() - 1;
	int64		fc_rank = 1;
	int64		fc_duplicate_count = 0;
	OSAPerGroupState *fc_osastate;
	int			fc_numDistinctCols;
	Datum		fc_abbrevVal = (Datum) 0;
	Datum		fc_abbrevOld = (Datum) 0;
	TupleTableSlot *fc_slot;
	TupleTableSlot *fc_extraslot;
	TupleTableSlot *fc_slot2;
	int			fc_i;

	Assert(AggCheckCallContext(fcinfo, NULL) == AGG_CONTEXT_AGGREGATE);

	/* 如果没有常规行，则排名总是 1 */
	if (PG_ARGISNULL(0))
		PG_RETURN_INT64(fc_rank);

	fc_osastate = (OSAPerGroupState *) PG_GETARG_POINTER(0);
	fc_econtext = fc_osastate->qstate->econtext;
	if (!fc_econtext)
	{
		MemoryContext fc_oldcontext;

		/* 确保我们在正确的父上下文中创建 econtext。 */
		fc_oldcontext = MemoryContextSwitchTo(fc_osastate->qstate->qcontext);
		fc_osastate->qstate->econtext = CreateStandaloneExprContext();
		fc_econtext = fc_osastate->qstate->econtext;
		MemoryContextSwitchTo(fc_oldcontext);
	}

	/* 调整 nargs 为直接（或聚合）参数的数量 */
	if (fc_nargs % 2 != 0)
		elog(ERROR, "wrong number of arguments in hypothetical-set function");
	fc_nargs /= 2;

	fc_hypothetical_check_argtypes(fcinfo, fc_nargs, fc_osastate->qstate->tupdesc);

	/*
	 * 在比较元组时，我们可以省略标志列，因为我们只会
	 * 比较标志为 0 的行。
	 */
	fc_numDistinctCols = fc_osastate->qstate->numSortCols - 1;

	/* 构建元组比较器，如果我们还没有构建的话 */
	fc_compareTuple = fc_osastate->qstate->compareTuple;
	if (fc_compareTuple == NULL)
	{
		AttrNumber *fc_sortColIdx = fc_osastate->qstate->sortColIdx;
		MemoryContext fc_oldContext;

		fc_oldContext = MemoryContextSwitchTo(fc_osastate->qstate->qcontext);
		fc_compareTuple = execTuplesMatchPrepare(fc_osastate->qstate->tupdesc,
											  fc_numDistinctCols,
											  fc_sortColIdx,
											  fc_osastate->qstate->eqOperators,
											  fc_osastate->qstate->sortCollations,
											  NULL);
		MemoryContextSwitchTo(fc_oldContext);
		fc_osastate->qstate->compareTuple = fc_compareTuple;
	}

	/* 因为我们需要一个假设行，所以不能共享过渡状态 */
	Assert(!fc_osastate->sort_done);

	/* 将假设行插入排序中 */
	fc_slot = fc_osastate->qstate->tupslot;
	ExecClearTuple(fc_slot);
	for (fc_i = 0; fc_i < fc_nargs; fc_i++)
	{
		fc_slot->tts_values[fc_i] = PG_GETARG_DATUM(fc_i + 1);
		fc_slot->tts_isnull[fc_i] = PG_ARGISNULL(fc_i + 1);
	}
	fc_slot->tts_values[fc_i] = Int32GetDatum(-1);
	fc_slot->tts_isnull[fc_i] = false;
	ExecStoreVirtualTuple(fc_slot);

	tuplesort_puttupleslot(fc_osastate->sortstate, fc_slot);

	/* 完成排序 */
	tuplesort_performsort(fc_osastate->sortstate);
	fc_osastate->sort_done = true;

	/*
	 * 我们交替在 tupslot 和 extraslot 中获取数据，以便我们可以
	 * 使前一行可用于比较。这是通过
	 * 在每一行之后交换插槽指针变量来实现的。
	 */
	fc_extraslot = MakeSingleTupleTableSlot(fc_osastate->qstate->tupdesc,
										 &TTSOpsMinimalTuple);
	fc_slot2 = fc_extraslot;

	/* 迭代，直到找到假设行 */
	while (tuplesort_gettupleslot(fc_osastate->sortstate, true, true, fc_slot,
								  &fc_abbrevVal))
	{
		bool		fc_isnull;
		Datum		fc_d = slot_getattr(fc_slot, fc_nargs + 1, &fc_isnull);
		TupleTableSlot *fc_tmpslot;

		if (!fc_isnull && DatumGetInt32(fc_d) != 0)
			break;

		/* 计算非重复元组 */
		fc_econtext->ecxt_outertuple = fc_slot;
		fc_econtext->ecxt_innertuple = fc_slot2;

		if (!TupIsNull(fc_slot2) &&
			fc_abbrevVal == fc_abbrevOld &&
			ExecQualAndReset(fc_compareTuple, fc_econtext))
			fc_duplicate_count++;

		fc_tmpslot = fc_slot2;
		fc_slot2 = fc_slot;
		fc_slot = fc_tmpslot;
		/* 通过重用简化的键避免ExecQual()调用 */
		fc_abbrevOld = fc_abbrevVal;

		fc_rank++;

		CHECK_FOR_INTERRUPTS();
	}

	ExecClearTuple(fc_slot);
	ExecClearTuple(fc_slot2);

	ExecDropSingleTupleTableSlot(fc_extraslot);

	fc_rank = fc_rank - fc_duplicate_count;

	PG_RETURN_INT64(fc_rank);
}
